import errno
import logging
import select
import socket

LOG = logging.getLogger(__name__)

"""
eventmask

EPOLLIN         Available for read
EPOLLOUT        Available for write
EPOLLPRI        Urgent data for read
EPOLLERR        Error condition happened on the assoc. fd
EPOLLHUP        Hang up happened on the assoc. fd
EPOLLET         Set Edge Trigger behavior, the default is Level Trigger behavior
EPOLLONESHOT    Set one-shot behavior. After one event is pulled out, the fd is internally disabled
EPOLLEXCLUSIVE  Wake only one epoll object when the associated fd has an event. The default (if this flag is not set) is to wake all epoll objects polling on a fd.
EPOLLRDHUP      Stream socket peer closed connection or shut down writing half of connection.
EPOLLRDNORM     Equivalent to EPOLLIN
EPOLLRDBAND     Priority data band can be read.
EPOLLWRNORM     Equivalent to EPOLLOUT
EPOLLWRBAND     Priority data may be written.
EPOLLMSG        Ignored.

New in version 3.6: EPOLLEXCLUSIVE was added. It’s only supported by Linux Kernel 4.5 or later.
"""
_EVENTMASKS = 'IN OUT PRI ERR HUP ET ONESHOT EXCLUSIVE RDHUP RDNORM RDBAND WRNORM WRBAND MSG'
_EVENTMASKS = {'EPOLL' + i for i in _EVENTMASKS.split(' ')}
EVENTMASKS = {i: getattr(select, i) for i in _EVENTMASKS}
EVENTMASKS_R = {v: k for k, v in EVENTMASKS.items()}
# {'IN': 1, 'OUT': 4, 'PRI': 2, 'ERR': 8, 'HUP': 16, 'ET': 2147483648,
#  'ONESHOT': 1073741824, 'EXCLUSIVE': 268435456, 'RDHUP': 8192,
#  'RDNORM': 64, 'RDBAND': 128, 'WRNORM': 256, 'WRBAND': 512, 'MSG': 1024}
MASKS = [1 << _ for _ in range(31, -1, -1)]


def mask(num):
    result = []
    for i in MASKS:
        if num < i:
            continue
        result.append(i)
        num -= i
    return result


def is_server_alived(sock):
    """ Return True if this socket is connected.
    https://stackoverflow.com/a/48272460
    适合用在客户端
    在服务器上，只能表示服务器是否断开与客户端连接，效果估计和 sock._closed 一样
    """
    if not sock:
        LOG.debug('is_socket_valid 0: not socket instance')
        return False

    try:
        sock_name = sock.getsockname()
        LOG.debug('is_socket_valid 1: %s', sock_name)
    except socket.error as err:
        err_type = err.args[0]
        if err_type == errno.EBADF:  # 9: Bad file descriptor
            return False

    try:
        peer_name = sock.getpeername()
        LOG.debug('is_socket_valid 2: %s', sock_name)
    except socket.error as err:
        err_type = err.args[0]
        if err_type in [errno.EBADF, errno.ENOTCONN]:  # 9: Bad file descriptor.
            return False                               # 107: Transport endpoint is not connected

    return True


def is_socket_valid(sock):
    try:
        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_WINDOW_CLAMP, 1000000)
        LOG.debug('is_socket_valid: sock.setsockopt')
        return True
    except Exception:
        return False
